package ca.usask.cs.decisiontree;

import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.JOptionPane;

import ca.usask.cs.decisiontree.api.EventEdgeInterface;
import ca.usask.cs.decisiontree.api.EventNodeInterface;
import ca.usask.cs.decisiontree.api.Edge;
import ca.usask.cs.exceptions.Exceptions;


/**
 * This class is not yet implemented.
 * @author Tony Leung, Alan Yeung
 * @editor Wenyi AN
 */
public class EventNode implements EventNodeInterface {
    
    private Edge sourceEdge;
    private double time;
    private String variableName;
    private boolean selected;
    private LinkedList<EventEdgeInterface> childrenEdgeList;
    private int treeNodeID;
    private double rollBackValue;
    private Exceptions exception;
 //   private String NodeType = "ChanceNode";
    
    public EventNode(int theTreeNodeID) {
        
        this.sourceEdge = null;
        this.time = Double.NaN;
        this.variableName = null;
        this.selected = false;
        this.childrenEdgeList = new LinkedList<EventEdgeInterface>();
        this.treeNodeID = theTreeNodeID;
        this.rollBackValue = Double.NaN;
        this.exception = new Exceptions();
    }
    /**
     * return the type of Node
     * @return NodeType
     */
//    public String getNodeType(){
//    	return this.NodeType;
//    }
    /**
     * method to return  value
     * @return  value in double
     */
    public double getRollBackValue()
    {
        return this.rollBackValue;
    }

    /**
     * method to set  value
     * @param double that represents  value
     */
    public void setRollBackValue(double newValue)
    {
        this.rollBackValue = newValue;
    }

    /**
     * retrieves the treeNode ID
     * @return treeNodeID in int
     */
    public int getTreeNodeID()
    {
        return this.treeNodeID;
    }
    
    /**
     * Indicates if the Chance node is selected
     * @return  true if selected
     *          false if not selected
     */
    public boolean isSelected()
    {
        return this.selected;
    }

    /**
     * Assigns the current Node as selected or not selected
     * @param   true if selected
     *          false if not selected
     * @param newSelected
     */
    public void setSelected(boolean newSelected)
    {
        this.selected = newSelected;
    }
    
    /**
     * Retrieves the time of the current Node
     * @return
     */
    public double getTime()
    {
        return this.time;
    }

        /**
     * Retrieve the time value in double format
     *
     * @precondition the time must be >=0
     * @param sourceEdge An Edge with this node as the destination.
     * @see Edge
     */
    public void setTime(double newTime) throws IllegalArgumentException
    {
        if(newTime < 0)
        {
        	JOptionPane.showMessageDialog(null,"The given Time is invalid. Please Ensure the assigned time is greater than 0");
            throw new IllegalArgumentException("The given Time is invalid. Please Ensure the assigned time is greater than 0");
        }
        else
        {
            this.time = newTime;
        }
    }

    /**
     * Retrieves the variable name of the chance node
     * @return the variable name in string format
     */
    public String getVariableName()
    {
        return this.variableName;
    }

    /**
     * Assigns the Variable name of the intermediate tree node
     */
    public void setVariableName(String newVariableName)
    {
        this.variableName = newVariableName;
    }

    
    /**
     * Remove an Edge from this node to disconnect a child. If the Edge does not exist, do nothing.
     * Note: No exception is thrown if the Edge was not a child, as the removal is considered successful.
     * 
     * @postcondition iterator returned from getChildren will not contain childToRemove
     * 
     * @param childToRemove An Edge to attempt to remove from this node.
     * @see Edge
     */
    public boolean removeChildEdge(Edge childToRemove){
        return this.childrenEdgeList.remove(childToRemove);
    }

/**
     * Adds an Edge to this node to connect a child and set the edge's probability
     * to 0.0. If the Edge already exists
     * in the node, do nothing.
     *
     * Throws exception if:
     *          - the Edge's sourcce is not the current node.
     *          - the Edge's destination is equal to the current node
     *          - the Edge's destination is equal to null
     *          - the Edge is null
 *              - the Edge is not a type ChanceEdge
     *
     *
     * @postcondition iterator returned from getChildren will contain childToAdd
     *
     * @param childToAdd An Edge to be added to this node
     * @return
     */
    public boolean addChildEdge(Edge childToAdd) throws IllegalArgumentException
    {
    	try{
    		exception.nullEdgeCheck(childToAdd, "The new child \"" + childToAdd + "\" is invalid");
    	}
    	catch(IllegalArgumentException e){
    		System.out.println(e.getMessage());
    	}
        // ensures the edge being added must be a chance edge
        if((childToAdd.getEdgeType().equals("ChanceEdge")) == false)
        {
            throw new IllegalArgumentException("All child edges of a chance node must be a chanceEdge");
        }
        if(childToAdd.getSourceNode() != this
                || childToAdd.getDestinationNode() == this
                || childToAdd.getDestinationNode() == null)
        {
        	JOptionPane.showMessageDialog(null,"The new child \"" + childToAdd + "\" is invalid");
            throw new IllegalArgumentException("The new child \"" + childToAdd + "\" is invalid");
        }
        if(this.childrenEdgeList.contains(childToAdd))
        {
            //do nothing
            return false;
        }
        // add the child to the list and set their probability to 0.0
        else
        {
            EventEdgeInterface tempEdge = (EventEdgeInterface) childToAdd;
            tempEdge.setProbability(0.0);
            this.childrenEdgeList.add(tempEdge);
            return true;
        }
    }

    /**
     * Adds an Edge to this node to connect a child. Change the childToAdd's
     * probability to probability. If the Edge already exists
     * in the node, do nothing.
     *
     * Throws exception if:
     *          - the Edge's sourcce is not the current node.
     *          - the Edge's destination is equal to the current node
     *          - the Edge's destination is equal to null
     *          - the Edge is null
     *
     *
     * @postcondition iterator returned from getChildren will contain childToAdd
     *
     * @param childToAdd An Edge to be added to this node
     * @return
     */
    public boolean addChildEdge(Edge childToAdd, double probability) throws IllegalArgumentException
    {
    	try{
    		exception.nullEdgeCheck(childToAdd, "The new child \"" + childToAdd + "\" is invalid");
    	}
    	catch(IllegalArgumentException e){
    		System.out.println(e.getMessage());
    	}
        // ensures the edge being added must be a chance edge
        if((childToAdd.getEdgeType().equals("ChanceEdge")) == false)
        {
            throw new IllegalArgumentException("All child edges of a chance node must be a chanceEdge");
        }
                if(childToAdd.getSourceNode() != this
                || childToAdd.getDestinationNode() == this
                || childToAdd.getDestinationNode() == null)
        {
            throw new IllegalArgumentException("The new child \"" + childToAdd + "\" is invalid");
        }
        if(probability < 0.0 || probability > 1.0)
        {
            throw new IllegalArgumentException("The probability of " + probability +" exceeds 1");
        }
        if((getTotalProbability() + probability) > 1.0)
        {
            throw new IllegalArgumentException("The total probability of the child Edges exceed 1");
        }

        if(this.childrenEdgeList.contains(childToAdd))
        {
            //do nothing
            return false;
        }
        else
        {
            EventEdgeInterface tempEdge = (EventEdgeInterface) childToAdd;
            tempEdge.setProbability(probability);
            this.childrenEdgeList.add(tempEdge);
            return true;
        }
    }

    /**
     * retrieves the sum of all Probabilities of all child Edge's
     * @return
     */
    public double getTotalProbability()
    {
        double total;
        Iterator<Edge> edgeIterator = getChildEdgeList();
        total = 0.0;
        EventEdgeInterface tempEdge;

        while(edgeIterator.hasNext())
        {
            tempEdge = (EventEdgeInterface) edgeIterator.next();
            total = total + tempEdge.getProbability();
        }
        return total;
    }

    /**
     * Retrieve the Edges which are children of this node.
     * 
     * @return An Iterator over Edges which have this node as their Source.
     * @see Iterator
     * @see Edge
     */
    public Iterator<Edge> getChildEdgeList(){

        //return this.childrenEdgeList.iterator();
        Iterator<EventEdgeInterface> getChildrenIterator = this.childrenEdgeList.iterator();

        LinkedList<Edge> tempChildEdgeList = new LinkedList<Edge>();
        while(getChildrenIterator.hasNext())
        {
            tempChildEdgeList.add(getChildrenIterator.next());
        }
        return tempChildEdgeList.iterator();
    }
    
    /**
     * Retrieve the Edge which connects this TreeNode to its parent TreeNode.
     * 
     * @return An Edge with this TreeNode as its destination. Will return NULL if no source Edge exists.
     * @see Edge
     */
    public Edge getSourceEdge(){
        return this.sourceEdge;
    }
    
    /**
     * Set a new source Edge for this TreeNode. The Edge must have this TreeNode as its destination.
     * throw IllegalArgumentException if
     *      - newSourceEdge is null
     *      - the destination of the Edge is not the current node
     *      - the newSourceEdge is a child Edge of the current NODe
     * @postcondition getSourceEdge returns the value provided as an argument
     *
     * @param sourceEdge An Edge with this node as the destination.
     * @see Edge
     */
    public void setSourceEdge(Edge newSourceEdge) throws IllegalArgumentException{
    	try{
    		exception.nullEdgeCheck(newSourceEdge, "Source Edge of a ChanceNode cannot be null");
    	}
    	catch(IllegalArgumentException e){
    		System.out.println(e.getMessage());
    	}
        if(newSourceEdge.getDestinationNode() != this)
        {

            throw new IllegalArgumentException("Source Edge destination node does not equal to Chance node");
        }
        else if(this.childrenEdgeList.contains(newSourceEdge))
        {
            throw new IllegalArgumentException("The SourceEdge is a child Edge of the Decision Node");
        }

        this.sourceEdge = newSourceEdge;
    }

    /**
     * returns true if the total probability of the ChanceEdges is 1.0
     *         false otherwise
     * @return
     */
    public boolean isValidTotalProbability() {
        double total;
        total = this.getTotalProbability();
        if(total == 1.0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
	@Override
	public void DecisionNodeCheck() {
		// TODO Auto-generated method stub
        throw new IllegalArgumentException("Source Node of a basicEdge cannot be a Chance Node");
	}
	@Override
	public void notTerminalNodeCheck(String msg) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void ChanceNodeCheck(String msg) {
		// TODO Auto-generated method stub
		
	}
    

}
